# tasks.py
from celery_app.celery_app import app
from config import Config
import requests
import datetime
import os
from pathlib import Path

import time
import logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),  # 输出到控制台
    ]
)
logger = logging.getLogger(__name__)


def filter_and_sort_alarms(alarms):
    """
    处理报警数据：
    1. 对同类别的报警去重，保留置信度最高的记录
    2. 按照预设的报警级别排序

    参数:
        alarms: 原始报警列表，每个元素包含class和confidence等字段

    返回:
        处理后的报警列表
    """
    # 1. 首先按类别分组，并保留每组中置信度最高的记录
    alarm_dict = {}
    for alarm in alarms:
        class_name = alarm["class"]
        # 如果该类报警不存在，或当前报警置信度更高，则更新
        if class_name not in alarm_dict or alarm["confidence"] > alarm_dict[class_name]["confidence"]:
            alarm_dict[class_name] = alarm

    # 2. 将字典转换回列表
    filtered_alarms = list(alarm_dict.values())

    # 3. 按照预设的报警级别排序 (从高到低)
    # 注意：这里使用Config中定义的级别，如果没有定义则默认给一个较低级别
    filtered_alarms.sort(key=lambda x: Config.ALARM_API["levels"].get(x["class"], 99))

    return filtered_alarms



@app.task(bind=True, max_retries=3, name='send_alarm_task')
def send_alarm_task(task, alarm_data):
    """
    处理报警发送任务
    新参数格式:
    {
        "area_id": "area1",
        "alarms": [
            {
                "class": "持械",
                "max_confidence": 0.85,
                "timestamp": time.time(),
                "frame_path": "/path/to/image.jpg"  # 可选
            }
        ],
        "files": []  # 将由任务自动生成
    }
    """
    # 1. 数据验证
    area_id = alarm_data["area_id"]
    raw_alarms = alarm_data.get("alarms", [])
    file_objects = []
    temp_files = []  # To keep track of opened files for cleanup
    try:

        # 1. 过滤和排序报警数据
        alarms = filter_and_sort_alarms(raw_alarms)

        if not alarms:
            logger.warning(f"[{task.request.id}] 空报警数据: {area_id}")
            return {"status": "skipped", "reason": "empty alarms"}

        # 2. 准备文件上传（符合RestrictedFileField要求）
        for alarm in alarms:
            if not alarm.get("frame_path"):
                continue

            try:
                file_path = Path(alarm["frame_path"])
                if file_path.exists():
                    # Open file without using 'with' so it stays open
                    f = open(file_path, 'rb')
                    temp_files.append(f)  # Keep reference for later cleanup
                    file_objects.append(
                        ('files', (
                            file_path.name,
                            f,
                            'image/jpeg'  # 明确指定Content-Type
                        ))
                    )
            except Exception as e:
                logger.error(f"[{task.request.id}] 文件准备失败: {alarm['frame_path']} - {str(e)}")

        # 3. 构建请求数据（保持原结构，优化消息内容）
        primary_alarm = alarms[0]  # 已按level排序，第一个就是最高优先级的

        # 生成报警消息（包含所有类别）
        if len(alarms) == 1:
            alarm_msg = f"区域{area_id}发现{primary_alarm['class']}事件"
        else:
            other_alarms = [a['class'] for a in alarms[1:]]
            alarm_msg = f"区域{area_id}发现{primary_alarm['class']}等{len(alarms)}类事件（含：{'、'.join(other_alarms)}）"

        payload = {
            "type": Config.ALARM_API["types"].get(primary_alarm['class'], 0),
            "msg": alarm_msg,  # 优化后的消息
            "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "level": Config.ALARM_API["levels"].get(primary_alarm['class'], 3),
            "file_type": 1,
            "stream_url": Config.CAMERAS.get(area_id, {}).get("stream_url", "")
        }

        logger.info(
            f"[{task.request.id}] 准备发送报警 | "
            f"区域: {area_id} | 主类型: {primary_alarm['class']} | "
            f"总类别数: {len(alarms)} | 文件数: {len(file_objects)}"
        )

        # 4. 发送请求
        response = requests.post(
            Config.ALARM_API["endpoint"],
            data=payload,
            files=file_objects,
            timeout=15  # 增加超时时间
        )
        response.raise_for_status()

        # 5. 处理响应
        result = response.json()
        logger.info(f"[{task.request.id}] 报警提交成功 | 响应: {result}")
        return result

    except requests.exceptions.RequestException as e:
        logger.error(f"[{task.request.id}] API请求失败: {str(e)}")
        raise task.retry(exc=e, countdown=60)
    except Exception as e:
        logger.error(f"[{task.request.id}] 任务处理异常: {str(e)}", exc_info=True)
        return {"status": "error", "message": str(e)}
    finally:
        # 6. 清理临时文件和关闭文件句柄
        for f in temp_files:
            try:
                f.close()
            except Exception as e:
                logger.warning(f"[{task.request.id}] 文件关闭失败: {str(e)}")

        # for alarm in alarms:
        #     frame_path = alarm.get("frame_path")
        #     if frame_path and Path(frame_path).exists():
        #         try:
        #             Path(frame_path).unlink()
        #             logger.debug(f"[{task.request.id}] 已清理文件: {frame_path}")
        #         except Exception as e:
        #             logger.warning(f"[{task.request.id}] 文件删除失败: {frame_path} - {str(e)}")
